<?php

declare(strict_types=1);

/**
 * This file is part of Hyperf.
 *
 * @link     https://www.hyperf.io
 * @document https://hyperf.wiki
 * @contact  group@hyperf.io
 * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
 */

use App\Model\EmailLog;
use App\Model\SmsLog;
use App\Model\SyncFile;
use App\Utils\Enum\RedisEnum;
use Hyperf\Context\ApplicationContext;
use Hyperf\Filesystem\FilesystemFactory;
use Hyperf\Guzzle\ClientFactory;
use Hyperf\HttpServer\Contract\RequestInterface;
use League\Flysystem\FilesystemException;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use function Hyperf\Support\env;

/**
 * 同步远程文件
 * @throws Exception
 */
function syncFile($url, $timeout = 5)
{
    if (!str_starts_with($url, 'http')) {
        throw new Exception('文件地址不正确');
    }
    $syncFile = SyncFile::query()->where('target_url', $url)->first();
    if ($syncFile) {
        return $syncFile['local_url'];
    }

    try {
        $container = ApplicationContext::getContainer();
        $client = $container->get(ClientFactory::class)->create();
        $res = $client->get($url, ['timeout' => $timeout]);
        if ($res->getStatusCode() !== 200) {
            throw new Exception('文件下载返回状态不正确(code:' . $res->getStatusCode() . ')');
        }
        $ext = fileext($url);
        $filename = date('Y/md/') . md5($url) . '.' . $ext;
        $cos = $container->get(FilesystemFactory::class)->get('cos');
        $cos->write('img/' . $filename, $res->getBody()->getContents());
        $ret = SyncFile::create([
            'local_url' => $filename,
            'target_url' => $url,
            'fileext' => $ext,
            'filesize' => 0,
            'add_time' => time(),
        ]);
        if ($ret) {
            return $filename;
        }
        throw new Exception('同步失败');
    } catch (NotFoundExceptionInterface|ContainerExceptionInterface|FilesystemException $e) {
        throw new Exception('初始化错误:' . $e->getMessage());
    }
}

function parsePageParams($input, $defaultPageSize = 20): array
{
    $pageNum = max(1, isset($input['pageNum']) ? intval($input['pageNum']) : 0);
    $pageSize = isset($input['pageSize']) ? intval($input['pageSize']) : $defaultPageSize;
    $offset = $pageSize > 0 ? ($pageNum - 1) * $pageSize : 0;
    return [$pageNum, $pageSize, $offset];
}

/**
 * 图片地址补全
 * @param $url
 * @param null $width
 * @return string
 */
function img_url($url, $width = null): string
{
    if (empty($url)) {
        return '';
    }
    if (!str_starts_with($url, 'http')) {
        $isCOS = true;
        $url = env('COS_URL') . $url;
    }
    if (!is_null($width)) {
        if (str_contains($url, 'myqcloud.com') || isset($isCOS)) {
            $url .= "?imageMogr2/thumbnail/{$width}x";
        }
    }
    return $url;
}

/**
 * 手机号码脱敏.
 * @param mixed $phone
 * @return null|array|string|string[]
 */
function desensitizePhone($phone)
{
    // 对手机号进行脱敏处理，比如只显示前3位和后4位
    return preg_replace('/(\d{3})\d{4}(\d{4})/', '$1****$2', $phone);
}

/**
 * 获取客户端IP.
 * @return mixed|string
 */
function getClientIp(): mixed
{
    try {
        $request = ApplicationContext::getContainer()->get(RequestInterface::class);

        $res = $request->getHeaders();
        if (isset($res['http_client_ip'][0])) {
            return $res['http_client_ip'][0];
        }
        if (isset($res['x-real-ip'])) {
            return $res['x-real-ip'][0];
        }
        if (isset($res['x-forwarded-for'])) {
            return $res['x-forwarded-for'][0];
        }
        if (isset($res['http_x_forwarded_for'])) {
            $arr = explode(',', $res['http_x_xforwarded_for'][0]);
            return $arr[0];
        }
        $serverParams = $request->getServerParams();
        return $serverParams['remote_addr'] ?? '';
    } catch (ContainerExceptionInterface|NotFoundExceptionInterface $e) {
        return '';
    }
}

/**
 * 生成密码
 * @param mixed $password
 * @param mixed $salt
 * @param mixed $has_md5
 */
function create_password($password, $salt, $has_md5 = true): string
{
    if (!$has_md5) {
        $password = md5($password);
    }
    return md5($password . $salt);
}

/**
 * 验证码图形验证码
 * @param mixed $id
 * @param mixed $code
 */
function validate_captcha($id, $code): bool
{
    try {
        $redis = ApplicationContext::getContainer()->get(Redis::class);
        $val = $redis->get(RedisEnum::PREFIX_CAPTCHA . $id);
        var_dump(RedisEnum::PREFIX_CAPTCHA . $id);
        return $code === $redis->get(RedisEnum::PREFIX_CAPTCHA . $id);
    } catch (ContainerExceptionInterface|NotFoundExceptionInterface|RedisException $e) {
        echo $e->getMessage();
    }
    return false;
}

/**
 * 验证码手机验证码
 * (验证成功返回发送记录ID，便于后续标记已验证).
 * @param mixed $scene
 * @param mixed $mobile
 * @param mixed $code
 */
function validate_sms_code($scene, $mobile, $code): bool|int
{
    $smsLog = SmsLog::query()->where([['mobile', '=', $mobile], ['scene', '=', $scene]])->orderBy('id', 'DESC')->first();
    if ($smsLog && $smsLog['validate_time'] == 0 && $smsLog['validate_code'] == $code) {
        return (int)$smsLog['id'];
    }
    return false;
}

/**
 * 验证邮箱验证码
 *  (验证成功返回发送记录ID，便于后续标记已验证).
 * @param mixed $scene
 * @param mixed $email
 * @param mixed $code
 */
function validate_email_code($scene, $email, $code): bool|int
{
    $emailLog = EmailLog::query()->where([['email', '=', $email], ['scene', '=', $scene]])->orderBy('id', 'DESC')->first();
    if ($emailLog && $emailLog['validate_time'] == 0 && $emailLog['validate_code'] == $code) {
        return (int)$emailLog['id'];
    }
    return false;
}

/**
 * 取得文件扩展.
 *
 * @param $filename string 文件名
 * @return string 扩展名
 */
function fileext($filename)
{
    return strtolower(trim(substr(strrchr($filename, '.'), 1, 10)));
}

/**
 * 对象转JSON字符串.
 * @param mixed $data
 * @return false|string
 */
function new_json_encode($data)
{
    return \json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}

/**
 * JSON字符串转对象或数组.
 * @param mixed $is_object
 * @return array|mixed|stdClass
 */
function new_json_decode(string|null $string, $is_object = false)
{
    if (empty($string)) {
        return $is_object ? new stdClass() : [];
    }
    return \json_decode($string, !$is_object);
}

/**
 * Function dataformat
 * 时间转换.
 * @param $n INT 时间
 */
function dataformat($n)
{
    $hours = floor($n / 3600);
    $minite = floor($n % 3600 / 60);
    $secend = floor($n % 3600 % 60);
    $minite = $minite < 10 ? '0' . $minite : $minite;
    $secend = $secend < 10 ? '0' . $secend : $secend;
    if ($n >= 3600) {
        return $hours . ':' . $minite . ':' . $secend;
    }
    return $minite . ':' . $secend;
}

/**
 * 人性化时间.
 * @param mixed $timeInt
 */
function time_format($timeInt, string $format = 'Y-m-d H:i:s'): string
{
    if (empty($timeInt) || !is_numeric($timeInt) || !$timeInt) {
        return '';
    }
    $d = time() - $timeInt;
    if ($d < 0) {
        return '';
    }
    if ($d < 60) {
        return $d . '秒前';
    }
    if ($d < 3600) {
        return floor($d / 60) . '分钟前';
    }
    if ($d < 86400) {
        return floor($d / 3600) . '小时前';
    }
    if ($d < 259200) {// 3天内
        return floor($d / 86400) . '天前';
    }
    return date($format, $timeInt);
}

// RSA生成
function rsa_new()
{
    $config = [
        'private_key_bits' => 2048, // 位数
        'private_key_type' => OPENSSL_KEYTYPE_RSA,
    ];
    $res = openssl_pkey_new($config);
    openssl_pkey_export($res, $privKey); // 私钥
    $pubKey = openssl_pkey_get_details($res);
    $pubKey = $pubKey['key']; // 公钥
    return ['privKey' => $privKey, 'pubKey' => $pubKey];
}

// RSA2签名
function rsa_sign($data, $priKey)
{
    if (is_file($priKey)) {
        $priKey = file_get_contents($priKey);
    }
    $res = openssl_get_privatekey($priKey);
    openssl_sign($data, $sign, $res, OPENSSL_ALGO_SHA256);
    openssl_free_key($res);
    return base64_encode($sign);
}

// RSA2验签
function rsa_verify($data, $pubKey, $sign)
{
    if (is_file($pubKey)) {
        $pubKey = file_get_contents($pubKey);
    }
    $res = openssl_get_publickey($pubKey);
    $result = (bool)openssl_verify($data, base64_decode($sign), $res, OPENSSL_ALGO_SHA256);
    openssl_free_key($res);
    return $result;
}

// RSA2解密
function rsa_decrypt($encStr, $priKey)
{
    if (is_file($priKey)) {
        $priKey = file_get_contents($priKey);
    }
    $private_key = openssl_get_privatekey($priKey);
    $original_enc_str = base64_decode($encStr);
    $orig_dec_str = '';
    openssl_private_decrypt($original_enc_str, $orig_dec_str, $private_key);
    return $orig_dec_str;
}

/**
 * RSA2加密.
 * @param mixed $str
 * @param mixed $publicKey
 */
function rsa_encrypt($str, $publicKey): string
{
    if (is_file($publicKey)) {
        $publicKey = file_get_contents($publicKey);
    }
    $public_key = openssl_get_publickey($publicKey);
    $enstr = '';
    openssl_public_encrypt($str, $enstr, $public_key);
    return $enstr;
}

/**
 * AES-128-CBC ENCODE.
 * @param mixed $data
 * @param mixed $key
 * @param mixed $iv
 */
function aes_128_cbc_encode($data, $key, $iv): string
{
    return base64_encode(openssl_encrypt($data, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv));
}

/**
 * AES-128-CBC 解密.
 * @param mixed $data
 * @param mixed $key
 * @param mixed $iv
 */
function aes_128_cbc_decode($data, $key, $iv): false|string
{
    return openssl_decrypt(base64_decode($data), 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
}

function getmicrotime()
{
    [$usec, $sec] = explode(' ', microtime());
    return (float)$usec + (float)$sec;
}

/**
 * 生成随机字符串.
 * @param string $lenth 长度
 * @return string 字符串
 */
function create_randomstr($lenth = 6): string
{
    return random($lenth, '123456789abcdefghijklmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ');
}

/**
 * 产生随机字符串.
 *
 * @param int $length 输出长度
 * @param string $chars 可选的 ，默认为 0123456789
 * @return string 字符串
 */
function random($length, $chars = '0123456789')
{
    $hash = '';
    $max = strlen($chars) - 1;
    mt_srand();
    for ($i = 0; $i < $length; ++$i) {
        $hash .= $chars[mt_rand(0, $max)];
    }
    return $hash;
}

/**
 * 返回经addslashes处理过的字符串或数组.
 * @param mixed $string
 * @return array|string
 */
function new_addslashes($string)
{
    if (!is_array($string)) {
        return addslashes($string);
    }
    foreach ($string as $key => $val) {
        $string[$key] = new_addslashes($val);
    }
    return $string;
}

/**
 * 返回经stripslashes处理过的字符串或数组.
 * @param $string string 需要处理的字符串或数组
 * @return array|string
 */
function new_stripslashes($string): array|string
{
    if (!is_array($string)) {
        return stripslashes($string);
    }
    foreach ($string as $key => $val) {
        $string[$key] = new_stripslashes($val);
    }
    return $string;
}

/**
 * 手机号码脱敏
 * @param $mobile
 * @return string
 */
function maskMobile($mobile): string
{
    return empty($mobile) ? $mobile : substr_replace($mobile, '****', 3, 4);
}

/**
 * 邮箱脱敏
 * @param $email
 * @return string
 */
function maskEmail($email): string
{
    if (empty($email)) {
        return $email;
    }
    $emailParts = explode('@', $email);
    $domainParts = explode('.', $emailParts[1]);
    $stars = str_repeat('*', strlen($emailParts[0]) - 1);
    return $emailParts[0][0] . $stars . '@' . $domainParts[0][0] . '**.' . $domainParts[1];
}

/**
 * 校验UUID格式
 * @param $uuid
 * @return false|int
 */
function isUUID($uuid): false|int
{
    return preg_match('/^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$/', $uuid);
}

/**
 * 是否是手机号码
 * @param $mobile
 * @return bool
 */
function is_mobile($mobile): bool
{
    return (bool)preg_match('/^1\d{10}$/', $mobile);
}

/**
 * 是否为邮箱地址
 * @param $email
 * @return bool
 */
function is_email($email): bool
{
    return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}

/**
 * 文件大小格式化
 * @param $bytes
 * @param $decimals
 * @return string
 */
function formatSize($bytes, $decimals = 2): string
{
    $units = ['B', 'KB', 'MB', 'GB', 'TB'];
    $factor = floor(log($bytes, 1024)); // 使用 log 函数计算单位
    $factor = min($factor, count($units) - 1); // 确保单位不超出范围
    return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . ' ' . $units[$factor];
}